package io.gitee.horizon007.webspider.metadata;

import lombok.Data;

import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

@Data
public class PropertyMetadata {

    private final ClassMetadata owner;

    private final String name;
    private final Type genericType;
    private final Class<?> classType;
    private final MethodHandle writeMethod;
    private final Map<Class<? extends Annotation>, Annotation> annotationMap;

    private final List<? extends Class<?>> genericParameterList;

    public PropertyMetadata(ClassMetadata owner, String name, Type genericType, Class<?> classType, MethodHandle writeMethod, Map<Class<? extends Annotation>, Annotation> annotationMap) {
        this.owner = owner;
        this.name = name;
        this.genericType = genericType;
        this.classType = classType;
        this.writeMethod = writeMethod;
        this.annotationMap = annotationMap;

        if (isArray()) {
            this.genericParameterList = List.of(classType.getComponentType());
        } else if (genericType instanceof ParameterizedType parameterizedType) {
            this.genericParameterList = Arrays.stream(parameterizedType.getActualTypeArguments())
                                              .map(v -> v instanceof Class<?> a ? a : null)
                                              .filter(Objects::nonNull)
                                              .toList();
        } else {
            this.genericParameterList = Collections.emptyList();
        }
    }

    @SuppressWarnings("unchecked")
    public <A> A getAnnotation(Class<A> annotationType) {
        if (annotationType == null || annotationMap == null) {
            return null;
        }
        return (A) annotationMap.get(annotationType);
    }

    public boolean isArray() {
        return classType.isArray();
    }

    public boolean isList() {
        return classType == List.class;
    }

    public boolean isMap() {
        return classType == Map.class;
    }

}
